package org.example.camundaapp.utils;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @className: ProcessDiagramUtil
 * @author: ZHRJ
 * @date: 2025-06-30 17:21
 * @Version: 1.0
 * @description:
 */
@Slf4j
public class ProcessDiagramUtil {

    /**
     * 生成带高亮（已执行、当前节点）的SVG流程图
     * @param processInstanceId
     * @return
     * @throws Exception
     */
    public static byte[] generateHighlightedSvg(
            HistoryService historyService,
            RuntimeService runtimeService,
            RepositoryService repositoryService,
            String processInstanceId
    ) throws Exception {
        // 1. 找到流程定义
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(
                        historyService.createHistoricProcessInstanceQuery()
                                .processInstanceId(processInstanceId)
                                .singleResult()
                                .getProcessDefinitionId())
                .singleResult();

        // 2. 读取BpmnModel
        InputStream bpmnStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName());
        BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
        BpmnModel bpmnModel = xmlConverter.convertToBpmnModel(
                () -> bpmnStream, false, false);

        // 3. 已执行节点（activityId）列表
        List<HistoricActivityInstance> hisActList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceId().asc()
                .list();
        List<String> highLightedActivities = hisActList.stream()
                .map(HistoricActivityInstance::getActivityId)
                .distinct()
                .collect(Collectors.toList());

        // 4. 已执行线（flowId）列表
        List<String> highLightedFlows = getHighLightedFlows(bpmnModel, hisActList);

        // 5. 当前活动节点
        /*List<String> currentActivityIds = runtimeService.createExecutionQuery()
                .processInstanceId(processInstanceId)
                .list()
                .stream()
                .map(execution -> execution.getActivityId())
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());*/
        List<String> currentActivityIds = runtimeService.getActiveActivityIds(processInstanceId);

        log.info("获取当前活动节点，流程实例ID：{}，当前节点列表：{}", processInstanceId, JSON.toJSONString(currentActivityIds));

        // 6. 生成PNG
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        /*InputStream svgInput = diagramGenerator.generateDiagram(
                bpmnModel,
                "svg",
                highLightedActivities,
                highLightedFlows,
                "宋体",
                "宋体",
                "宋体",
                1.0,
                currentActivityIds);*/ //  flowable-image-generator:6.8.1 不支持 currentActivityIds参数，升级版本的话jdk不支持；可以前端解析SVG，找到当前节点（你知道id），用JS或正则替换fill/stroke属性实现不同颜色高亮。
        InputStream svgInput = diagramGenerator.generateDiagram(
                bpmnModel,
                "png",
                highLightedActivities,
                highLightedFlows,
                "宋体",
                "宋体",
                "宋体",
                null,
                1.0,
                false);

        // 7. 返回PNG字符串
        //return new String(svgInput.readAllBytes(), StandardCharsets.UTF_8);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[8192];
        int length;
        while ((length = svgInput.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }
        return baos.toByteArray();
    }

    /**
     * 获取已执行的流程连线（flowId）列表，适配并发/分支等复杂流程（获取高亮流程节点）
     * @param bpmnModel
     * @param historicActivityInstances
     * @return
     */
    private static List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        List<String> highFlows = new ArrayList<>();
        Map<String, HistoricActivityInstance> activityMap = new HashMap<>();
        for (HistoricActivityInstance hai : historicActivityInstances) {
            activityMap.put(hai.getId(), hai);
        }
        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
            HistoricActivityInstance curr = historicActivityInstances.get(i);
            HistoricActivityInstance next = historicActivityInstances.get(i + 1);

            if (curr.getActivityType().equals("sequenceFlow")) {
                continue; // 跳过连线节点
            }
            FlowNode currNode = (FlowNode) bpmnModel.getFlowElement(curr.getActivityId());
            if (currNode == null) continue;
            for (org.flowable.bpmn.model.SequenceFlow sf : currNode.getOutgoingFlows()) {
                if (sf.getTargetRef().equals(next.getActivityId())) {
                    highFlows.add(sf.getId());
                }
            }
        }
        return highFlows.stream().distinct().collect(Collectors.toList());
    }
}
